/**
* Owl carousel
* @version 2.0.0
* @author Bartosz Wojciechowski
* @license The MIT License (MIT)
* @todo Lazy Load Icon
* @todo prevent animationend bubling
* @todo itemsScaleUp
* @todo Test Zepto
* @todo stagePadding calculate wrong active classes
*/
(function ( $, window, document, undefined ) {

    var drag, state, e;

    /**
     * Template for status information about drag and touch events.
     * @private
     */
    drag = {
        start    : 0,
        startX   : 0,
        startY   : 0,
        current  : 0,
        currentX : 0,
        currentY : 0,
        offsetX  : 0,
        offsetY  : 0,
        distance : null,
        startTime: 0,
        endTime  : 0,
        updatedX : 0,
        targetEl : null
    };

    /**
     * Template for some status informations.
     * @private
     */
    state = {
        isTouch    : false,
        isScrolling: false,
        isSwiping  : false,
        direction  : false,
        inMotion   : false
    };

    /**
     * Event functions references.
     * @private
     */
    e = {
        _onDragStart   : null,
        _onDragMove    : null,
        _onDragEnd     : null,
        _transitionEnd : null,
        _resizer       : null,
        _responsiveCall: null,
        _goToLoop      : null,
        _checkVisibile : null
    };

    /**
     * Creates a carousel.
     * @class The Owl Carousel.
     * @public
     * @param {HTMLElement|jQuery} element - The element to create the carousel for.
     * @param {Object} [options] - The options
     */
    function Owl( element, options ) {

        /**
         * Current settings for the carousel.
         * @public
         */
        this.settings = null;

        /**
         * Current options set by the caller including defaults.
         * @public
         */
        this.options = $.extend( {}, Owl.Defaults, options );

        /**
         * Plugin element.
         * @public
         */
        this.$element = $( element );

        /**
         * Caches informations about drag and touch events.
         */
        this.drag = $.extend( {}, drag );

        /**
         * Caches some status informations.
         * @protected
         */
        this.state = $.extend( {}, state );

        /**
         * @protected
         * @todo Must be documented
         */
        this.e = $.extend( {}, e );

        /**
         * References to the running plugins of this carousel.
         * @protected
         */
        this._plugins = {};

        /**
         * Currently suppressed events to prevent them from beeing retriggered.
         * @protected
         */
        this._supress = {};

        /**
         * Absolute current position.
         * @protected
         */
        this._current = null;

        /**
         * Animation speed in milliseconds.
         * @protected
         */
        this._speed = null;

        /**
         * Coordinates of all items in pixel.
         * @todo The name of this member is missleading.
         * @protected
         */
        this._coordinates = [];

        /**
         * Current breakpoint.
         * @todo Real media queries would be nice.
         * @protected
         */
        this._breakpoint = null;

        /**
         * Current width of the plugin element.
         */
        this._width = null;

        /**
         * All real items.
         * @protected
         */
        this._items = [];

        /**
         * All cloned items.
         * @protected
         */
        this._clones = [];

        /**
         * Merge values of all items.
         * @todo Maybe this could be part of a plugin.
         * @protected
         */
        this._mergers = [];

        /**
         * Invalidated parts within the update process.
         * @protected
         */
        this._invalidated = {};

        /**
         * Ordered list of workers for the update process.
         * @protected
         */
        this._pipe = [];

        $.each(
            Owl.Plugins, $.proxy(
                function ( key, plugin ) {
                    this._plugins[ key[ 0 ].toLowerCase() + key.slice( 1 ) ]
                        = new plugin( this );
                }, this
            )
        );

        $.each(
            Owl.Pipe, $.proxy(
                function ( priority, worker ) {
                    this._pipe.push(
                        {
                            'filter': worker.filter,
                            'run'   : $.proxy( worker.run, this )
                        }
                    );
                }, this
            )
        );

        this.setup();
        this.initialize();
    }

    /**
     * Default options for the carousel.
     * @public
     */
    Owl.Defaults = {
        items                : 3,
        loop                 : false,
        center               : false,
        mouseDrag            : true,
        touchDrag            : true,
        pullDrag             : true,
        freeDrag             : false,
        margin               : 0,
        stagePadding         : 0,
        merge                : false,
        mergeFit             : true,
        autoWidth            : false,
        startPosition        : 0,
        rtl                  : false,
        smartSpeed           : 250,
        fluidSpeed           : false,
        dragEndSpeed         : false,
        responsive           : {},
        responsiveRefreshRate: 200,
        responsiveBaseElement: window,
        responsiveClass      : false,
        fallbackEasing       : 'swing',
        info                 : false,
        nestedItemSelector   : false,
        itemElement          : 'div',
        stageElement         : 'div',
        // Classes and Names
        themeClass           : 'owl-theme',
        baseClass            : 'owl-carousel',
        itemClass            : 'owl-item',
        centerClass          : 'center',
        activeClass          : 'active'
    };

    /**
     * Enumeration for width.
     * @public
     * @readonly
     * @enum {String}
     */
    Owl.Width = {
        Default: 'default',
        Inner  : 'inner',
        Outer  : 'outer'
    };

    /**
     * Contains all registered plugins.
     * @public
     */
    Owl.Plugins = {};

    /**
     * Update pipe.
     */
    Owl.Pipe = [ {
        filter: [ 'width', 'items', 'settings' ],
        run   : function ( cache ) {
            cache.current = this._items
                && this._items[ this.relative( this._current ) ];
        }
    }, {
        filter: [ 'items', 'settings' ],
        run   : function () {
            var cached = this._clones,
                clones = this.$stage.children( '.cloned' );

            if ( clones.length !== cached.length || ( ! this.settings.loop
                && cached.length > 0 ) ) {
                this.$stage.children( '.cloned' ).remove();
                this._clones = [];
            }
        }
    }, {
        filter: [ 'items', 'settings' ],
        run   : function () {
            var i, n,
                clones = this._clones,
                items  = this._items,
                delta  = this.settings.loop ?
                clones.length - Math.max( this.settings.items * 2, 4 ) : 0;

            for ( i = 0, n = Math.abs( delta / 2 ); i < n; i ++ ) {
                if ( delta > 0 ) {
                    this.$stage.children().eq(
                        items.length + clones.length
                        - 1
                    ).remove();
                    clones.pop();
                    this.$stage.children().eq( 0 ).remove();
                    clones.pop();
                }
                else {
                    clones.push( clones.length / 2 );
                    this.$stage.append(
                        items[ clones[ clones.length
                        - 1 ] ].clone().addClass( 'cloned' )
                    );
                    clones.push( items.length - 1 - ( clones.length - 1 ) / 2 );
                    this.$stage.prepend(
                        items[ clones[ clones.length
                        - 1 ] ].clone().addClass( 'cloned' )
                    );
                }
            }
        }
    }, {
        filter: [ 'width', 'items', 'settings' ],
        run   : function () {
            var rtl        = ( this.settings.rtl ? 1 : - 1 ),
                width      = ( this.width() / this.settings.items ).toFixed( 3 ),
                coordinate = 0, merge, i, n;

            this._coordinates = [];
            for ( i = 0, n = this._clones.length + this._items.length; i < n; i ++ ) {
                merge = this._mergers[ this.relative( i ) ];
                merge = ( this.settings.mergeFit
                    && Math.min( merge, this.settings.items ) ) || merge;
                coordinate += ( this.settings.autoWidth ?
                    this._items[ this.relative( i ) ].width() + this.settings.margin :
                    width * merge ) * rtl;

                this._coordinates.push( coordinate );
            }
        }
    }, {
        filter: [ 'width', 'items', 'settings' ],
        run   : function () {
            var i, n, width                           = ( this.width()
            / this.settings.items ).toFixed( 3 ), css = {
                'width'        : Math.abs(
                    this._coordinates[ this._coordinates.length
                    - 1 ]
                ) + this.settings.stagePadding * 2,
                'padding-left' : this.settings.stagePadding || '',
                'padding-right': this.settings.stagePadding || ''
            };

            this.$stage.css( css );

            css                  = {
                'width': this.settings.autoWidth ?
                    'auto' :
                width - this.settings.margin
            };
            css[ this.settings.rtl ? 'margin-left' :
                'margin-right' ] = this.settings.margin;

            if ( ! this.settings.autoWidth && $.grep(
                    this._mergers, function ( v ) {
                        return v > 1
                    }
                ).length > 0 ) {
                for ( i = 0, n = this._coordinates.length; i < n; i ++ ) {
                    css.width = Math.abs( this._coordinates[ i ] )
                        - Math.abs( this._coordinates[ i - 1 ] || 0 ) - this.settings.margin;
                    this.$stage.children().eq( i ).css( css );
                }
            }
            else {
                this.$stage.children().css( css );
            }
        }
    }, {
        filter: [ 'width', 'items', 'settings' ],
        run   : function ( cache ) {
            cache.current
            && this.reset( this.$stage.children().index( cache.current ) );
        }
    }, {
        filter: [ 'position' ],
        run   : function () {
            this.animate( this.coordinates( this._current ) );
        }
    }, {
        filter: [ 'width', 'position', 'items', 'settings' ],
        run   : function () {
            var rtl                   = this.settings.rtl ? 1 : - 1,
                padding               = this.settings.stagePadding * 2,
                begin                 = this.coordinates( this.current() ) + padding,
                end                   = begin + this.width() * rtl,
                inner, outer, matches = [], i, n;

            for ( i = 0, n = this._coordinates.length; i < n; i ++ ) {
                inner = this._coordinates[ i - 1 ] || 0;
                outer = Math.abs( this._coordinates[ i ] ) + padding * rtl;

                if ( ( this.op( inner, '<=', begin ) && ( this.op( inner, '>', end ) ) )
                    || ( this.op( outer, '<', begin ) && this.op( outer, '>', end ) ) ) {
                    matches.push( i );
                }
            }

            this.$stage.children(
                '.'
                + this.settings.activeClass
            ).removeClass( this.settings.activeClass );
            this.$stage.children(
                ':eq(' + matches.join( '), :eq(' )
                + ')'
            ).addClass( this.settings.activeClass );

            if ( this.settings.center ) {
                this.$stage.children(
                    '.'
                    + this.settings.centerClass
                ).removeClass( this.settings.centerClass );
                this.$stage.children().eq( this.current() ).addClass( this.settings.centerClass );
            }
        }
    } ];

    /**
     * Initializes the carousel.
     * @protected
     */
    Owl.prototype.initialize = function () {
        this.trigger( 'initialize' );

        this.$element
            .addClass( this.settings.baseClass )
            .addClass( this.settings.themeClass )
            .toggleClass( 'owl-rtl', this.settings.rtl );

        // check support
        this.browserSupport();

        if ( this.settings.autoWidth && this.state.imagesLoaded !== true ) {
            var imgs, nestedSelector, width;
            imgs           = this.$element.find( 'img' );
            nestedSelector = this.settings.nestedItemSelector ?
            '.' + this.settings.nestedItemSelector : undefined;
            width          = this.$element.children( nestedSelector ).width();

            if ( imgs.length && width <= 0 ) {
                this.preloadAutoWidthImages( imgs );
                return false;
            }
        }

        this.$element.addClass( 'owl-loading' );

        // create stage
        this.$stage = $( '<' + this.settings.stageElement + ' class="owl-stage"/>' )
            .wrap( '<div class="owl-stage-outer">' );

        // append stage
        this.$element.append( this.$stage.parent() );

        // append content
        this.replace( this.$element.children().not( this.$stage.parent() ) );

        // set view width
        this._width = this.$element.width();

        // update view
        this.refresh();

        this.$element.removeClass( 'owl-loading' ).addClass( 'owl-loaded' );

        // attach generic events
        this.eventsCall();

        // attach generic events
        this.internalEvents();

        // attach custom control events
        this.addTriggerableEvents();

        this.trigger( 'initialized' );
    };

    /**
     * Setups the current settings.
     * @todo Remove responsive classes. Why should adaptive designs be brought into IE8?
     * @todo Support for media queries by using `matchMedia` would be nice.
     * @public
     */
    Owl.prototype.setup = function () {
        var viewport   = this.viewport(),
            overwrites = this.options.responsive,
            match      = - 1,
            settings   = null;

        if ( ! overwrites ) {
            settings = $.extend( {}, this.options );
        }
        else {
            $.each(
                overwrites, function ( breakpoint ) {
                    if ( breakpoint <= viewport && breakpoint > match ) {
                        match = Number( breakpoint );
                    }
                }
            );

            settings = $.extend( {}, this.options, overwrites[ match ] );
            delete settings.responsive;

            // responsive class
            if ( settings.responsiveClass ) {
                this.$element.attr(
                    'class', function ( i, c ) {
                        return c.replace( /\b owl-responsive-\S+/g, '' );
                    }
                ).addClass( 'owl-responsive-' + match );
            }
        }

        if ( this.settings === null || this._breakpoint !== match ) {
            this.trigger(
                'change', {
                    property: {
                        name: 'settings', value: settings
                    }
                }
            );
            this._breakpoint = match;
            this.settings    = settings;
            this.invalidate( 'settings' );
            this.trigger(
                'changed', {
                    property: {
                        name: 'settings', value: this.settings
                    }
                }
            );
        }
    };

    /**
     * Updates option logic if necessery.
     * @protected
     */
    Owl.prototype.optionsLogic = function () {
        // Toggle Center class
        this.$element.toggleClass( 'owl-center', this.settings.center );

        // if items number is less than in body
        if ( this.settings.loop && this._items.length < this.settings.items ) {
            this.settings.loop = false;
        }

        if ( this.settings.autoWidth ) {
            this.settings.stagePadding = false;
            this.settings.merge        = false;
        }
    };

    /**
     * Prepares an item before add.
     * @todo Rename event parameter `content` to `item`.
     * @protected
     * @returns {jQuery|HTMLElement} - The item container.
     */
    Owl.prototype.prepare = function ( item ) {
        var event = this.trigger(
            'prepare', {
                content: item
            }
        );

        if ( ! event.data ) {
            event.data = $( '<' + this.settings.itemElement + '/>' )
                .addClass( this.settings.itemClass ).append( item )
        }

        this.trigger(
            'prepared', {
                content: event.data
            }
        );

        return event.data;
    };

    /**
     * Updates the view.
     * @public
     */
    Owl.prototype.update = function () {
        var i      = 0,
            n      = this._pipe.length,
            filter = $.proxy(
                function ( p ) {
                    return this[ p ]
                }, this._invalidated
            ),
            cache  = {};

        while ( i < n ) {
            if ( this._invalidated.all
                || $.grep( this._pipe[ i ].filter, filter ).length > 0 ) {
                this._pipe[ i ].run( cache );
            }
            i ++;
        }

        this._invalidated = {};
    };

    /**
     * Gets the width of the view.
     * @public
     * @param {Owl.Width} [dimension=Owl.Width.Default] - The dimension to return.
     * @returns {Number} - The width of the view in pixel.
     */
    Owl.prototype.width = function ( dimension ) {
        dimension = dimension || Owl.Width.Default;
        switch ( dimension ) {
            case Owl.Width.Inner:
            case Owl.Width.Outer:
                return this._width;
            default:
                return this._width - this.settings.stagePadding * 2 + this.settings.margin;
        }
    };

    /**
     * Refreshes the carousel primarily for adaptive purposes.
     * @public
     */
    Owl.prototype.refresh = function () {
        if ( this._items.length === 0 ) {
            return false;
        }

        var start = new Date().getTime();

        this.trigger( 'refresh' );

        this.setup();

        this.optionsLogic();

        // hide and show methods helps here to set a proper widths,
        // this prevents scrollbar to be calculated in stage width
        this.$stage.addClass( 'owl-refresh' );

        this.update();

        this.$stage.removeClass( 'owl-refresh' );

        this.state.orientation = window.orientation;

        this.watchVisibility();

        this.trigger( 'refreshed' );
    };

    /**
     * Save internal event references and add event based functions.
     * @protected
     */
    Owl.prototype.eventsCall = function () {
        // Save events references
        this.e._onDragStart   = $.proxy(
            function ( e ) {
                this.onDragStart( e );
            }, this
        );
        this.e._onDragMove    = $.proxy(
            function ( e ) {
                this.onDragMove( e );
            }, this
        );
        this.e._onDragEnd     = $.proxy(
            function ( e ) {
                this.onDragEnd( e );
            }, this
        );
        this.e._onResize      = $.proxy(
            function ( e ) {
                this.onResize( e );
            }, this
        );
        this.e._transitionEnd = $.proxy(
            function ( e ) {
                this.transitionEnd( e );
            }, this
        );
        this.e._preventClick  = $.proxy(
            function ( e ) {
                this.preventClick( e );
            }, this
        );
    };

    /**
     * Checks window `resize` event.
     * @protected
     */
    Owl.prototype.onThrottledResize = function () {
        window.clearTimeout( this.resizeTimer );
        this.resizeTimer =
            window.setTimeout( this.e._onResize, this.settings.responsiveRefreshRate );
    };

    /**
     * Checks window `resize` event.
     * @protected
     */
    Owl.prototype.onResize = function () {
        if ( ! this._items.length ) {
            return false;
        }

        if ( this._width === this.$element.width() ) {
            return false;
        }

        if ( this.trigger( 'resize' ).isDefaultPrevented() ) {
            return false;
        }

        this._width = this.$element.width();

        this.invalidate( 'width' );

        this.refresh();

        this.trigger( 'resized' );
    };

    /**
     * Checks for touch/mouse drag event type and add run event handlers.
     * @protected
     */
    Owl.prototype.eventsRouter = function ( event ) {
        var type = event.type;

        if ( type === "mousedown" || type === "touchstart" ) {
            this.onDragStart( event );
        }
        else if ( type === "mousemove" || type === "touchmove" ) {
            this.onDragMove( event );
        }
        else if ( type === "mouseup" || type === "touchend" ) {
            this.onDragEnd( event );
        }
        else if ( type === "touchcancel" ) {
            this.onDragEnd( event );
        }
    };

    /**
     * Checks for touch/mouse drag options and add necessery event handlers.
     * @protected
     */
    Owl.prototype.internalEvents = function () {
        var isTouch   = isTouchSupport(),
            isTouchIE = isTouchSupportIE();

        if ( this.settings.mouseDrag ) {
            this.$stage.on(
                'mousedown', $.proxy(
                    function ( event ) {
                        this.eventsRouter( event )
                    }, this
                )
            );
            this.$stage.on(
                'dragstart', function () {
                    return false
                }
            );
            this.$stage.get( 0 ).onselectstart = function () {
                return false
            };
        }
        else {
            this.$element.addClass( 'owl-text-select-on' );
        }

        if ( this.settings.touchDrag && ! isTouchIE ) {
            this.$stage.on(
                'touchstart touchcancel', $.proxy(
                    function ( event ) {
                        this.eventsRouter( event )
                    }, this
                )
            );
        }

        // catch transitionEnd event
        if ( this.transitionEndVendor ) {
            this.on( this.$stage.get( 0 ), this.transitionEndVendor, this.e._transitionEnd, false );
        }

        // responsive
        if ( this.settings.responsive !== false ) {
            this.on( window, 'resize', $.proxy( this.onThrottledResize, this ) );
        }
    };

    /**
     * Handles touchstart/mousedown event.
     * @protected
     * @param {Event} event - The event arguments.
     */
    Owl.prototype.onDragStart = function ( event ) {
        var ev, isTouchEvent, pageX, pageY, animatedPos;

        ev                     = event.originalEvent || event || window.event;

        // prevent right click
        if ( ev.which === 3 || this.state.isTouch ) {
            return false;
        }

        if ( ev.type === 'mousedown' ) {
            this.$stage.addClass( 'owl-grab' );
        }

        this.trigger( 'drag' );
        this.drag.startTime    = new Date().getTime();
        this.speed( 0 );
        this.state.isTouch     = true;
        this.state.isScrolling = false;
        this.state.isSwiping   = false;
        this.drag.distance     = 0;

        pageX = getTouches( ev ).x;
        pageY = getTouches( ev ).y;

        // get stage position left
        this.drag.offsetX = this.$stage.position().left;
        this.drag.offsetY = this.$stage.position().top;

        if ( this.settings.rtl ) {
            this.drag.offsetX = this.$stage.position().left + this.$stage.width()
                - this.width()
                + this.settings.margin;
        }

        // catch position // ie to fix
        if ( this.state.inMotion && this.support3d ) {
            animatedPos         = this.getTransformProperty();
            this.drag.offsetX   = animatedPos;
            this.animate( animatedPos );
            this.state.inMotion = true;
        }
        else if ( this.state.inMotion && ! this.support3d ) {
            this.state.inMotion = false;
            return false;
        }

        this.drag.startX = pageX - this.drag.offsetX;
        this.drag.startY = pageY - this.drag.offsetY;

        this.drag.start    = pageX - this.drag.startX;
        this.drag.targetEl = ev.target || ev.srcElement;
        this.drag.updatedX = this.drag.start;

        // to do/check
        // prevent links and images dragging;
        if ( this.drag.targetEl.tagName === "IMG" || this.drag.targetEl.tagName
            === "A" ) {
            this.drag.targetEl.draggable = false;
        }

        $( document ).on(
            'mousemove.owl.dragEvents mouseup.owl.dragEvents touchmove.owl.dragEvents touchend.owl.dragEvents', $.proxy(
                function ( event ) {
                    this.eventsRouter( event )
                }, this
            )
        );
    };

    /**
     * Handles the touchmove/mousemove events.
     * @todo Simplify
     * @protected
     * @param {Event} event - The event arguments.
     */
    Owl.prototype.onDragMove = function ( event ) {
        var ev, isTouchEvent, pageX, pageY, minValue, maxValue, pull;

        if ( ! this.state.isTouch ) {
            return;
        }

        if ( this.state.isScrolling ) {
            return;
        }

        ev = event.originalEvent || event || window.event;

        pageX = getTouches( ev ).x;
        pageY = getTouches( ev ).y;

        // Drag Direction
        this.drag.currentX = pageX - this.drag.startX;
        this.drag.currentY = pageY - this.drag.startY;
        this.drag.distance = this.drag.currentX - this.drag.offsetX;

        // Check move direction
        if ( this.drag.distance < 0 ) {
            this.state.direction = this.settings.rtl ? 'right' : 'left';
        }
        else if ( this.drag.distance > 0 ) {
            this.state.direction = this.settings.rtl ? 'left' : 'right';
        }
        // Loop
        if ( this.settings.loop ) {
            if ( this.op( this.drag.currentX, '>', this.coordinates( this.minimum() ) )
                && this.state.direction
                === 'right' ) {
                this.drag.currentX -= ( this.settings.center
                    && this.coordinates( 0 ) ) - this.coordinates( this._items.length );
            }
            else if ( this.op( this.drag.currentX, '<', this.coordinates( this.maximum() ) )
                && this.state.direction
                === 'left' ) {
                this.drag.currentX += ( this.settings.center
                    && this.coordinates( 0 ) ) - this.coordinates( this._items.length );
            }
        }
        else {
            // pull
            minValue           = this.settings.rtl ? this.coordinates( this.maximum() ) :
                this.coordinates( this.minimum() );
            maxValue           = this.settings.rtl ? this.coordinates( this.minimum() ) :
                this.coordinates( this.maximum() );
            pull               = this.settings.pullDrag ? this.drag.distance / 5 : 0;
            this.drag.currentX = Math.max(
                Math.min(
                    this.drag.currentX, minValue
                    + pull
                ), maxValue + pull
            );
        }

        // Lock browser if swiping horizontal

        if ( ( this.drag.distance > 8 || this.drag.distance < - 8 ) ) {
            if ( ev.preventDefault !== undefined ) {
                ev.preventDefault();
            }
            else {
                ev.returnValue = false;
            }
            this.state.isSwiping = true;
        }

        this.drag.updatedX = this.drag.currentX;

        // Lock Owl if scrolling
        if ( ( this.drag.currentY > 16 || this.drag.currentY < - 16 )
            && this.state.isSwiping === false ) {
            this.state.isScrolling = true;
            this.drag.updatedX     = this.drag.start;
        }

        this.animate( this.drag.updatedX );
    };

    /**
     * Handles the touchend/mouseup events.
     * @protected
     */
    Owl.prototype.onDragEnd = function ( event ) {
        var compareTimes, distanceAbs, closest;

        if ( ! this.state.isTouch ) {
            return;
        }

        if ( event.type === 'mouseup' ) {
            this.$stage.removeClass( 'owl-grab' );
        }

        this.trigger( 'dragged' );

        // prevent links and images dragging;
        this.drag.targetEl.removeAttribute( "draggable" );

        // remove drag event listeners

        this.state.isTouch     = false;
        this.state.isScrolling = false;
        this.state.isSwiping   = false;

        // to check
        if ( this.drag.distance === 0 && this.state.inMotion !== true ) {
            this.state.inMotion = false;
            return false;
        }

        // prevent clicks while scrolling

        this.drag.endTime = new Date().getTime();
        compareTimes      = this.drag.endTime - this.drag.startTime;
        distanceAbs       = Math.abs( this.drag.distance );

        // to test
        if ( distanceAbs > 3 || compareTimes > 300 ) {
            this.removeClick( this.drag.targetEl );
        }

        closest = this.closest( this.drag.updatedX );

        this.speed( this.settings.dragEndSpeed || this.settings.smartSpeed );
        this.current( closest );
        this.invalidate( 'position' );
        this.update();

        // if pullDrag is off then fire transitionEnd event manually when stick
        // to border
        if ( ! this.settings.pullDrag && this.drag.updatedX
            === this.coordinates( closest ) ) {
            this.transitionEnd();
        }

        this.drag.distance = 0;

        $( document ).off( '.owl.dragEvents' );
    };

    /**
     * Attaches `preventClick` to disable link while swipping.
     * @protected
     * @param {HTMLElement} [target] - The target of the `click` event.
     */
    Owl.prototype.removeClick = function ( target ) {
        this.drag.targetEl = target;
        $( target ).on( 'click.preventClick', this.e._preventClick );
        // to make sure click is removed:
        window.setTimeout(
            function () {
                $( target ).off( 'click.preventClick' );
            }, 300
        );
    };

    /**
     * Suppresses click event.
     * @protected
     * @param {Event} ev - The event arguments.
     */
    Owl.prototype.preventClick = function ( ev ) {
        if ( ev.preventDefault ) {
            ev.preventDefault();
        }
        else {
            ev.returnValue = false;
        }
        if ( ev.stopPropagation ) {
            ev.stopPropagation();
        }
        $( ev.target ).off( 'click.preventClick' );
    };

    /**
     * Catches stage position while animate (only CSS3).
     * @protected
     * @returns
     */
    Owl.prototype.getTransformProperty = function () {
        var transform, matrix3d;

        transform =
            window.getComputedStyle( this.$stage.get( 0 ), null ).getPropertyValue(
                this.vendorName
                + 'transform'
            );
        // var transform = this.$stage.css(this.vendorName + 'transform')
        transform = transform.replace( /matrix(3d)?\(|\)/g, '' ).split( ',' );
        matrix3d  = transform.length === 16;

        return matrix3d !== true ? transform[ 4 ] : transform[ 12 ];
    };

    /**
     * Gets absolute position of the closest item for a coordinate.
     * @todo Setting `freeDrag` makes `closest` not reusable. See #165.
     * @protected
     * @param {Number} coordinate - The coordinate in pixel.
     * @return {Number} - The absolute position of the closest item.
     */
    Owl.prototype.closest = function ( coordinate ) {
        var position = - 1, pull = 30, width = this.width(), coordinates =
            this.coordinates();

        if ( ! this.settings.freeDrag ) {
            // check closest item
            $.each(
                coordinates, $.proxy(
                    function ( index, value ) {
                        if ( coordinate > value - pull && coordinate < value + pull ) {
                            position = index;
                        }
                        else if ( this.op( coordinate, '<', value )
                            && this.op(
                                coordinate, '>', coordinates[ index + 1 ] || value
                                - width
                            ) ) {
                            position = this.state.direction === 'left' ? index + 1 : index;
                        }
                        return position === - 1;
                    }, this
                )
            );
        }

        if ( ! this.settings.loop ) {
            // non loop boundries
            if ( this.op( coordinate, '>', coordinates[ this.minimum() ] ) ) {
                position = coordinate = this.minimum();
            }
            else if ( this.op( coordinate, '<', coordinates[ this.maximum() ] ) ) {
                position = coordinate = this.maximum();
            }
        }

        return position;
    };

    /**
     * Animates the stage.
     * @public
     * @param {Number} coordinate - The coordinate in pixels.
     */
    Owl.prototype.animate = function ( coordinate ) {
        this.trigger( 'translate' );
        this.state.inMotion = this.speed() > 0;

        if ( this.support3d ) {
            this.$stage.css(
                {
                    transform : 'translate3d(' + coordinate + 'px' + ',0px, 0px)',
                    transition: ( this.speed() / 1000 ) + 's'
                }
            );
        }
        else if ( this.state.isTouch ) {
            this.$stage.css(
                {
                    left: coordinate + 'px'
                }
            );
        }
        else {
            this.$stage.animate(
                {
                    left: coordinate
                }, this.speed()
                / 1000, this.settings.fallbackEasing, $.proxy(
                    function () {
                        if ( this.state.inMotion ) {
                            this.transitionEnd();
                        }
                    }, this
                )
            );
        }
    };

    /**
     * Sets the absolute position of the current item.
     * @public
     * @param {Number} [position] - The new absolute position or nothing to leave it unchanged.
     * @returns {Number} - The absolute position of the current item.
     */
    Owl.prototype.current = function ( position ) {
        if ( position === undefined ) {
            return this._current;
        }

        if ( this._items.length === 0 ) {
            return undefined;
        }

        position = this.normalize( position );

        if ( this._current !== position ) {
            var event = this.trigger(
                'change', {
                    property: {
                        name: 'position', value: position
                    }
                }
            );

            if ( event.data !== undefined ) {
                position = this.normalize( event.data );
            }

            this._current = position;

            this.invalidate( 'position' );

            this.trigger(
                'changed', {
                    property: {
                        name: 'position', value: this._current
                    }
                }
            );
        }

        return this._current;
    };

    /**
     * Invalidates the given part of the update routine.
     * @param {String} part - The part to invalidate.
     */
    Owl.prototype.invalidate = function ( part ) {
        this._invalidated[ part ] = true;
    }

    /**
     * Resets the absolute position of the current item.
     * @public
     * @param {Number} position - The absolute position of the new item.
     */
    Owl.prototype.reset = function ( position ) {
        position = this.normalize( position );

        if ( position === undefined ) {
            return;
        }

        this._speed   = 0;
        this._current = position;

        this.suppress( [ 'translate', 'translated' ] );

        this.animate( this.coordinates( position ) );

        this.release( [ 'translate', 'translated' ] );
    };

    /**
     * Normalizes an absolute or a relative position for an item.
     * @public
     * @param {Number} position - The absolute or relative position to normalize.
     * @param {Boolean} [relative=false] - Whether the given position is relative or not.
     * @returns {Number} - The normalized position.
     */
    Owl.prototype.normalize = function ( position, relative ) {
        var n = ( relative ? this._items.length :
        this._items.length + this._clones.length );

        if ( ! $.isNumeric( position ) || n < 1 ) {
            return undefined;
        }

        if ( this._clones.length ) {
            position = ( ( position % n ) + n ) % n;
        }
        else {
            position =
                Math.max( this.minimum( relative ), Math.min( this.maximum( relative ), position ) );
        }

        return position;
    };

    /**
     * Converts an absolute position for an item into a relative position.
     * @public
     * @param {Number} position - The absolute position to convert.
     * @returns {Number} - The converted position.
     */
    Owl.prototype.relative = function ( position ) {
        position = this.normalize( position );
        position = position - this._clones.length / 2;
        return this.normalize( position, true );
    };

    /**
     * Gets the maximum position for an item.
     * @public
     * @param {Boolean} [relative=false] - Whether to return an absolute position or a relative position.
     * @returns {Number}
     */
    Owl.prototype.maximum = function ( relative ) {
        var maximum, width, i = 0, coordinate,
            settings          = this.settings;

        if ( relative ) {
            return this._items.length - 1;
        }

        if ( ! settings.loop && settings.center ) {
            maximum = this._items.length - 1;
        }
        else if ( ! settings.loop && ! settings.center ) {
            maximum = this._items.length - settings.items;
        }
        else if ( settings.loop || settings.center ) {
            maximum = this._items.length + settings.items;
        }
        else if ( settings.autoWidth || settings.merge ) {
            var revert = settings.rtl ? 1 : - 1;
            width      = this.$stage.width() - this.$element.width();
            while ( coordinate = this.coordinates( i ) ) {
                if ( coordinate * revert >= width ) {
                    break;
                }
                maximum = ++ i;
            }
        }
        else {
            throw 'Can not detect maximum absolute position.'
        }

        return maximum;
    };

    /**
     * Gets the minimum position for an item.
     * @public
     * @param {Boolean} [relative=false] - Whether to return an absolute position or a relative position.
     * @returns {Number}
     */
    Owl.prototype.minimum = function ( relative ) {
        if ( relative ) {
            return 0;
        }

        return this._clones.length / 2;
    };

    /**
     * Gets an item at the specified relative position.
     * @public
     * @param {Number} [position] - The relative position of the item.
     * @return {jQuery|Array.<jQuery>} - The item at the given position or all items if no position was given.
     */
    Owl.prototype.items = function ( position ) {
        if ( position === undefined ) {
            return this._items.slice();
        }

        position = this.normalize( position, true );
        return this._items[ position ];
    };

    /**
     * Gets an item at the specified relative position.
     * @public
     * @param {Number} [position] - The relative position of the item.
     * @return {jQuery|Array.<jQuery>} - The item at the given position or all items if no position was given.
     */
    Owl.prototype.mergers = function ( position ) {
        if ( position === undefined ) {
            return this._mergers.slice();
        }

        position = this.normalize( position, true );
        return this._mergers[ position ];
    };

    /**
     * Gets the absolute positions of clones for an item.
     * @public
     * @param {Number} [position] - The relative position of the item.
     * @returns {Array.<Number>} - The absolute positions of clones for the item or all if no position was given.
     */
    Owl.prototype.clones = function ( position ) {
        var odd  = this._clones.length / 2,
            even = odd + this._items.length,
            map  = function ( index ) {
                return index % 2 === 0 ? even + index / 2 : odd - ( index + 1 ) / 2
            };

        if ( position === undefined ) {
            return $.map(
                this._clones, function ( v, i ) {
                    return map( i )
                }
            );
        }

        return $.map(
            this._clones, function ( v, i ) {
                return v === position ? map( i ) : null
            }
        );
    };

    /**
     * Sets the current animation speed.
     * @public
     * @param {Number} [speed] - The animation speed in milliseconds or nothing to leave it unchanged.
     * @returns {Number} - The current animation speed in milliseconds.
     */
    Owl.prototype.speed = function ( speed ) {
        if ( speed !== undefined ) {
            this._speed = speed;
        }

        return this._speed;
    };

    /**
     * Gets the coordinate of an item.
     * @todo The name of this method is missleanding.
     * @public
     * @param {Number} position - The absolute position of the item within `minimum()` and `maximum()`.
     * @returns {Number|Array.<Number>} - The coordinate of the item in pixel or all coordinates.
     */
    Owl.prototype.coordinates = function ( position ) {
        var coordinate = null;

        if ( position === undefined ) {
            return $.map(
                this._coordinates, $.proxy(
                    function ( coordinate, index ) {
                        return this.coordinates( index );
                    }, this
                )
            );
        }

        if ( this.settings.center ) {
            coordinate = this._coordinates[ position ];
            coordinate += ( this.width() - coordinate + ( this._coordinates[ position
                - 1 ] || 0 ) ) / 2 * ( this.settings.rtl ?
                    - 1 :
                    1 );
        }
        else {
            coordinate = this._coordinates[ position - 1 ] || 0;
        }

        return coordinate;
    };

    /**
     * Calculates the speed for a translation.
     * @protected
     * @param {Number} from - The absolute position of the start item.
     * @param {Number} to - The absolute position of the target item.
     * @param {Number} [factor=undefined] - The time factor in milliseconds.
     * @returns {Number} - The time in milliseconds for the translation.
     */
    Owl.prototype.duration = function ( from, to, factor ) {
        return Math.min( Math.max( Math.abs( to - from ), 1 ), 6 )
            * Math.abs( ( factor || this.settings.smartSpeed ) );
    };

    /**
     * Slides to the specified item.
     * @public
     * @param {Number} position - The position of the item.
     * @param {Number} [speed] - The time in milliseconds for the transition.
     */
    Owl.prototype.to = function ( position, speed ) {
        if ( this.settings.loop ) {
            var distance     = position - this.relative( this.current() ),
                revert       = this.current(),
                before       = this.current(),
                after        = this.current() + distance,
                direction    = before - after < 0 ? true : false,
                items        = this._clones.length + this._items.length;

            if ( after < this.settings.items && direction === false ) {
                revert = before + this._items.length;
                this.reset( revert );
            }
            else if ( after >= items - this.settings.items && direction === true ) {
                revert = before - this._items.length;
                this.reset( revert );
            }
            window.clearTimeout( this.e._goToLoop );
            this.e._goToLoop = window.setTimeout(
                $.proxy(
                    function () {
                        this.speed( this.duration( this.current(), revert + distance, speed ) );
                        this.current( revert + distance );
                        this.update();
                    }, this
                ), 30
            );
        }
        else {
            this.speed( this.duration( this.current(), position, speed ) );
            this.current( position );
            this.update();
        }
    };

    /**
     * Slides to the next item.
     * @public
     * @param {Number} [speed] - The time in milliseconds for the transition.
     */
    Owl.prototype.next = function ( speed ) {
        speed = speed || false;
        this.to( this.relative( this.current() ) + 1, speed );
    };

    /**
     * Slides to the previous item.
     * @public
     * @param {Number} [speed] - The time in milliseconds for the transition.
     */
    Owl.prototype.prev = function ( speed ) {
        speed = speed || false;
        this.to( this.relative( this.current() ) - 1, speed );
    };

    /**
     * Handles the end of an animation.
     * @protected
     * @param {Event} event - The event arguments.
     */
    Owl.prototype.transitionEnd = function ( event ) {

        // if css2 animation then event object is undefined
        if ( event !== undefined ) {
            event.stopPropagation();

            // Catch only owl-stage transitionEnd event
            if ( ( event.target || event.srcElement || event.originalTarget )
                !== this.$stage.get( 0 ) ) {
                return false;
            }
        }

        this.state.inMotion = false;
        this.trigger( 'translated' );
    };

    /**
     * Gets viewport width.
     * @protected
     * @return {Number} - The width in pixel.
     */
    Owl.prototype.viewport = function () {
        var width;
        if ( this.options.responsiveBaseElement !== window ) {
            width = $( this.options.responsiveBaseElement ).width();
        }
        else if ( window.innerWidth ) {
            width = window.innerWidth;
        }
        else if ( document.documentElement
            && document.documentElement.clientWidth ) {
            width = document.documentElement.clientWidth;
        }
        else {
            throw 'Can not detect viewport width.';
        }
        return width;
    };

    /**
     * Replaces the current content.
     * @public
     * @param {HTMLElement|jQuery|String} content - The new content.
     */
    Owl.prototype.replace = function ( content ) {
        this.$stage.empty();
        this._items = [];

        if ( content ) {
            content = ( content instanceof jQuery ) ? content : $( content );
        }

        if ( this.settings.nestedItemSelector ) {
            content = content.find( '.' + this.settings.nestedItemSelector );
        }

        content.filter(
            function () {
                return this.nodeType === 1;
            }
        ).each(
            $.proxy(
                function ( index, item ) {
                    item = this.prepare( item );
                    this.$stage.append( item );
                    this._items.push( item );
                    this._mergers.push(
                        item.find( '[data-merge]' ).andSelf( '[data-merge]' ).attr( 'data-merge' )
                        * 1
                        || 1
                    );
                }, this
            )
        );

        this.reset(
            $.isNumeric( this.settings.startPosition ) ?
                this.settings.startPosition : 0
        );

        this.invalidate( 'items' );
    };

    /**
     * Adds an item.
     * @todo Use `item` instead of `content` for the event arguments.
     * @public
     * @param {HTMLElement|jQuery|String} content - The item content to add.
     * @param {Number} [position] - The relative position at which to insert the item otherwise the item will be added to
     *     the end.
     */
    Owl.prototype.add = function ( content, position ) {
        position = position === undefined ? this._items.length :
            this.normalize( position, true );

        this.trigger(
            'add', {
                content: content, position: position
            }
        );

        if ( this._items.length === 0 || position === this._items.length ) {
            this.$stage.append( content );
            this._items.push( content );
            this._mergers.push(
                content.find( '[data-merge]' ).andSelf( '[data-merge]' ).attr( 'data-merge' )
                * 1
                || 1
            );
        }
        else {
            this._items[ position ].before( content );
            this._items.splice( position, 0, content );
            this._mergers.splice(
                position, 0, content.find( '[data-merge]' ).andSelf( '[data-merge]' ).attr( 'data-merge' )
                * 1
                || 1
            );
        }

        this.invalidate( 'items' );

        this.trigger(
            'added', {
                content: content, position: position
            }
        );
    };

    /**
     * Removes an item by its position.
     * @todo Use `item` instead of `content` for the event arguments.
     * @public
     * @param {Number} position - The relative position of the item to remove.
     */
    Owl.prototype.remove = function ( position ) {
        position = this.normalize( position, true );

        if ( position === undefined ) {
            return;
        }

        this.trigger(
            'remove', {
                content: this._items[ position ], position: position
            }
        );

        this._items[ position ].remove();
        this._items.splice( position, 1 );
        this._mergers.splice( position, 1 );

        this.invalidate( 'items' );

        this.trigger(
            'removed', {
                content: null, position: position
            }
        );
    };

    /**
     * Adds triggerable events.
     * @protected
     */
    Owl.prototype.addTriggerableEvents = function () {
        var handler = $.proxy(
            function ( callback, event ) {
                return $.proxy(
                    function ( e ) {
                        if ( e.relatedTarget !== this ) {
                            this.suppress( [ event ] );
                            callback.apply( this, [].slice.call( arguments, 1 ) );
                            this.release( [ event ] );
                        }
                    }, this
                );
            }, this
        );

        $.each(
            {
                'next'   : this.next,
                'prev'   : this.prev,
                'to'     : this.to,
                'destroy': this.destroy,
                'refresh': this.refresh,
                'replace': this.replace,
                'add'    : this.add,
                'remove' : this.remove
            }, $.proxy(
                function ( event, callback ) {
                    this.$element.on(
                        event + '.owl.carousel', handler(
                            callback, event
                            + '.owl.carousel'
                        )
                    );
                }, this
            )
        );

    };

    /**
     * Watches the visibility of the carousel element.
     * @protected
     */
    Owl.prototype.watchVisibility = function () {

        // test on zepto
        if ( ! isElVisible( this.$element.get( 0 ) ) ) {
            this.$element.addClass( 'owl-hidden' );
            window.clearInterval( this.e._checkVisibile );
            this.e._checkVisibile =
                window.setInterval( $.proxy( checkVisible, this ), 500 );
        }

        function isElVisible( el ) {
            return el.offsetWidth > 0 && el.offsetHeight > 0;
        }

        function checkVisible() {
            if ( isElVisible( this.$element.get( 0 ) ) ) {
                this.$element.removeClass( 'owl-hidden' );
                this.refresh();
                window.clearInterval( this.e._checkVisibile );
            }
        }
    };

    /**
     * Preloads images with auto width.
     * @protected
     * @todo Still to test
     */
    Owl.prototype.preloadAutoWidthImages = function ( imgs ) {
        var loaded, that, $el, img;

        loaded = 0;
        that   = this;
        imgs.each(
            function ( i, el ) {
                $el = $( el );
                img = new Image();

                img.onload = function () {
                    loaded ++;
                    $el.attr( 'src', img.src );
                    $el.css( 'opacity', 1 );
                    if ( loaded >= imgs.length ) {
                        that.state.imagesLoaded = true;
                        that.initialize();
                    }
                };

                img.src = $el.attr( 'src' ) || $el.attr( 'data-src' )
                    || $el.attr( 'data-src-retina' );
            }
        );
    };

    /**
     * Destroys the carousel.
     * @public
     */
    Owl.prototype.destroy = function () {

        if ( this.$element.hasClass( this.settings.themeClass ) ) {
            this.$element.removeClass( this.settings.themeClass );
        }

        if ( this.settings.responsive !== false ) {
            $( window ).off( 'resize.owl.carousel' );
        }

        if ( this.transitionEndVendor ) {
            this.off( this.$stage.get( 0 ), this.transitionEndVendor, this.e._transitionEnd );
        }

        for ( var i in this._plugins ) {
            this._plugins[ i ].destroy();
        }

        if ( this.settings.mouseDrag || this.settings.touchDrag ) {
            this.$stage.off( 'mousedown touchstart touchcancel' );
            $( document ).off( '.owl.dragEvents' );
            this.$stage.get( 0 ).onselectstart = function () {
            };
            this.$stage.off(
                'dragstart', function () {
                    return false
                }
            );
        }

        // remove event handlers in the ".owl.carousel" namespace
        this.$element.off( '.owl' );

        this.$stage.children( '.cloned' ).remove();
        this.e = null;
        this.$element.removeData( 'owlCarousel' );

        this.$stage.children().contents().unwrap();
        this.$stage.children().unwrap();
        this.$stage.unwrap();
    };

    /**
     * Operators to calculate right-to-left and left-to-right.
     * @protected
     * @param {Number} [a] - The left side operand.
     * @param {String} [o] - The operator.
     * @param {Number} [b] - The right side operand.
     */
    Owl.prototype.op = function ( a, o, b ) {
        var rtl = this.settings.rtl;
        switch ( o ) {
            case '<':
                return rtl ? a > b : a < b;
            case '>':
                return rtl ? a < b : a > b;
            case '>=':
                return rtl ? a <= b : a >= b;
            case '<=':
                return rtl ? a >= b : a <= b;
            default:
                break;
        }
    };

    /**
     * Attaches to an internal event.
     * @protected
     * @param {HTMLElement} element - The event source.
     * @param {String} event - The event name.
     * @param {Function} listener - The event handler to attach.
     * @param {Boolean} capture - Wether the event should be handled at the capturing phase or not.
     */
    Owl.prototype.on = function ( element, event, listener, capture ) {
        if ( element.addEventListener ) {
            element.addEventListener( event, listener, capture );
        }
        else if ( element.attachEvent ) {
            element.attachEvent( 'on' + event, listener );
        }
    };

    /**
     * Detaches from an internal event.
     * @protected
     * @param {HTMLElement} element - The event source.
     * @param {String} event - The event name.
     * @param {Function} listener - The attached event handler to detach.
     * @param {Boolean} capture - Wether the attached event handler was registered as a capturing listener or not.
     */
    Owl.prototype.off = function ( element, event, listener, capture ) {
        if ( element.removeEventListener ) {
            element.removeEventListener( event, listener, capture );
        }
        else if ( element.detachEvent ) {
            element.detachEvent( 'on' + event, listener );
        }
    };

    /**
     * Triggers an public event.
     * @protected
     * @param {String} name - The event name.
     * @param {*} [data=null] - The event data.
     * @param {String} [namespace=.owl.carousel] - The event namespace.
     * @returns {Event} - The event arguments.
     */
    Owl.prototype.trigger = function ( name, data, namespace ) {
        var status = {
            item: {
                count: this._items.length, index: this.current()
            }
        }, handler = $.camelCase(
            $.grep(
                [ 'on', name, namespace ], function ( v ) {
                    return v
                }
            )
                .join( '-' ).toLowerCase()
        ), event   = $.Event(
            [ name, 'owl', namespace || 'carousel' ].join( '.' ).toLowerCase(),
            $.extend(
                {
                    relatedTarget: this
                }, status, data
            )
        );

        if ( ! this._supress[ name ] ) {
            $.each(
                this._plugins, function ( name, plugin ) {
                    if ( plugin.onTrigger ) {
                        plugin.onTrigger( event );
                    }
                }
            );

            this.$element.trigger( event );

            if ( this.settings && typeof this.settings[ handler ] === 'function' ) {
                this.settings[ handler ].apply( this, event );
            }
        }

        return event;
    };

    /**
     * Suppresses events.
     * @protected
     * @param {Array.<String>} events - The events to suppress.
     */
    Owl.prototype.suppress = function ( events ) {
        $.each(
            events, $.proxy(
                function ( index, event ) {
                    this._supress[ event ] = true;
                }, this
            )
        );
    }

    /**
     * Releases suppressed events.
     * @protected
     * @param {Array.<String>} events - The events to release.
     */
    Owl.prototype.release = function ( events ) {
        $.each(
            events, $.proxy(
                function ( index, event ) {
                    delete this._supress[ event ];
                }, this
            )
        );
    }

    /**
     * Checks the availability of some browser features.
     * @protected
     */
    Owl.prototype.browserSupport = function () {
        this.support3d = isPerspective();

        if ( this.support3d ) {
            this.transformVendor = isTransform();

            // take transitionend event name by detecting transition
            var endVendors           =
                [ 'transitionend', 'webkitTransitionEnd', 'transitionend', 'oTransitionEnd' ];
            this.transitionEndVendor = endVendors[ isTransition() ];

            // take vendor name from transform name
            this.vendorName = this.transformVendor.replace( /Transform/i, '' );
            this.vendorName = this.vendorName !== '' ?
            '-' + this.vendorName.toLowerCase() + '-' : '';
        }

        this.state.orientation = window.orientation;
    };

    /**
     * Get touch/drag coordinats.
     * @private
     * @param {event} - mousedown/touchstart event
     * @returns {object} - Contains X and Y of current mouse/touch position
     */

    function getTouches( event ) {
        if ( event.touches !== undefined ) {
            return {
                x: event.touches[ 0 ].pageX,
                y: event.touches[ 0 ].pageY
            };
        }

        if ( event.touches === undefined ) {
            if ( event.pageX !== undefined ) {
                return {
                    x: event.pageX,
                    y: event.pageY
                };
            }

            if ( event.pageX === undefined ) {
                return {
                    x: event.clientX,
                    y: event.clientY
                };
            }
        }
    }

    /**
     * Checks for CSS support.
     * @private
     * @param {Array} array - The CSS properties to check for.
     * @returns {Array} - Contains the supported CSS property name and its index or `false`.
     */
    function isStyleSupported( array ) {
        var p, s, fake = document.createElement( 'div' ), list = array;
        for ( p in list ) {
            s = list[ p ];
            if ( typeof fake.style[ s ] !== 'undefined' ) {
                fake = null;
                return [ s, p ];
            }
        }
        return [ false ];
    }

    /**
     * Checks for CSS transition support.
     * @private
     * @todo Realy bad design
     * @returns {Number}
     */
    function isTransition() {
        return isStyleSupported( [ 'transition', 'WebkitTransition', 'MozTransition', 'OTransition' ] )[ 1 ];
    }

    /**
     * Checks for CSS transform support.
     * @private
     * @returns {String} The supported property name or false.
     */
    function isTransform() {
        return isStyleSupported( [ 'transform', 'WebkitTransform', 'MozTransform', 'OTransform', 'msTransform' ] )[ 0 ];
    }

    /**
     * Checks for CSS perspective support.
     * @private
     * @returns {String} The supported property name or false.
     */
    function isPerspective() {
        return isStyleSupported( [ 'perspective', 'webkitPerspective', 'MozPerspective', 'OPerspective', 'MsPerspective' ] )[ 0 ];
    }

    /**
     * Checks wether touch is supported or not.
     * @private
     * @returns {Boolean}
     */
    function isTouchSupport() {
        return 'ontouchstart' in window || ! ! ( navigator.msMaxTouchPoints );
    }

    /**
     * Checks wether touch is supported or not for IE.
     * @private
     * @returns {Boolean}
     */
    function isTouchSupportIE() {
        return window.navigator.msPointerEnabled;
    }

    /**
     * The jQuery Plugin for the Owl Carousel
     * @public
     */
    $.fn.owlCarousel = function ( options ) {
        return this.each(
            function () {
                if ( ! $( this ).data( 'owlCarousel' ) ) {
                    $( this ).data( 'owlCarousel', new Owl( this, options ) );
                }
            }
        );
    };

    /**
     * The constructor for the jQuery Plugin
     * @public
     */
    $.fn.owlCarousel.Constructor = Owl;

})( window.Zepto || window.jQuery, window, document );

/**
 * Lazy Plugin
 * @version 2.0.0
 * @author Bartosz Wojciechowski
 * @license The MIT License (MIT)
 */
(function ( $, window, document, undefined ) {

    /**
     * Creates the lazy plugin.
     * @class The Lazy Plugin
     * @param {Owl} carousel - The Owl Carousel
     */
    var Lazy = function ( carousel ) {

        /**
         * Reference to the core.
         * @protected
         * @type {Owl}
         */
        this._core = carousel;

        /**
         * Already loaded items.
         * @protected
         * @type {Array.<jQuery>}
         */
        this._loaded = [];

        /**
         * Event handlers.
         * @protected
         * @type {Object}
         */
        this._handlers = {
            'initialized.owl.carousel change.owl.carousel': $.proxy(
                function ( e ) {
                    if ( ! e.namespace ) {
                        return;
                    }

                    if ( ! this._core.settings || ! this._core.settings.lazyLoad ) {
                        return;
                    }

                    if ( ( e.property && e.property.name == 'position' ) || e.type
                        == 'initialized' ) {
                        var settings = this._core.settings,
                            n        = ( settings.center && Math.ceil( settings.items / 2 )
                            || settings.items ),
                            i        = ( ( settings.center && n * - 1 ) || 0 ),
                            position = ( ( e.property && e.property.value )
                                || this._core.current() ) + i,
                            clones   = this._core.clones().length,
                            load     = $.proxy(
                                function ( i, v ) {
                                    this.load( v )
                                }, this
                            );

                        while ( i ++ < n ) {
                            this.load( clones / 2 + this._core.relative( position ) );
                            clones
                            && $.each( this._core.clones( this._core.relative( position ++ ) ), load );
                        }
                    }
                }, this
            )
        };

        // set the default options
        this._core.options = $.extend( {}, Lazy.Defaults, this._core.options );

        // register event handler
        this._core.$element.on( this._handlers );
    }

    /**
     * Default options.
     * @public
     */
    Lazy.Defaults = {
        lazyLoad: false
    }

    /**
     * Loads all resources of an item at the specified position.
     * @param {Number} position - The absolute position of the item.
     * @protected
     */
    Lazy.prototype.load = function ( position ) {
        var $item     = this._core.$stage.children().eq( position ),
            $elements = $item && $item.find( '.owl-lazy' );

        if ( ! $elements || $.inArray( $item.get( 0 ), this._loaded ) > - 1 ) {
            return;
        }

        $elements.each(
            $.proxy(
                function ( index, element ) {
                    var $element = $( element ), image,
                        url      = ( window.devicePixelRatio > 1
                            && $element.attr( 'data-src-retina' ) ) || $element.attr( 'data-src' );

                    this._core.trigger(
                        'load', {
                            element: $element, url: url
                        }, 'lazy'
                    );

                    if ( $element.is( 'img' ) ) {
                        $element.one(
                            'load.owl.lazy', $.proxy(
                                function () {
                                    $element.css( 'opacity', 1 );
                                    this._core.trigger(
                                        'loaded', {
                                            element: $element, url: url
                                        }, 'lazy'
                                    );
                                }, this
                            )
                        ).attr( 'src', url );
                    }
                    else {
                        image        = new Image();
                        image.onload = $.proxy(
                            function () {
                                $element.css(
                                    {
                                        'background-image': 'url(' + url + ')',
                                        'opacity'         : '1'
                                    }
                                );
                                this._core.trigger(
                                    'loaded', {
                                        element: $element, url: url
                                    }, 'lazy'
                                );
                            }, this
                        );
                        image.src    = url;
                    }
                }, this
            )
        );

        this._loaded.push( $item.get( 0 ) );
    }

    /**
     * Destroys the plugin.
     * @public
     */
    Lazy.prototype.destroy = function () {
        var handler, property;

        for ( handler in this.handlers ) {
            this._core.$element.off( handler, this.handlers[ handler ] );
        }
        for ( property in Object.getOwnPropertyNames( this ) ) {
            typeof this[ property ] != 'function' && ( this[ property ] = null );
        }
    }

    $.fn.owlCarousel.Constructor.Plugins.Lazy = Lazy;

})( window.Zepto || window.jQuery, window, document );

/**
 * AutoHeight Plugin
 * @version 2.0.0
 * @author Bartosz Wojciechowski
 * @license The MIT License (MIT)
 */
(function ( $, window, document, undefined ) {

    /**
     * Creates the auto height plugin.
     * @class The Auto Height Plugin
     * @param {Owl} carousel - The Owl Carousel
     */
    var AutoHeight = function ( carousel ) {
        /**
         * Reference to the core.
         * @protected
         * @type {Owl}
         */
        this._core = carousel;

        /**
         * All event handlers.
         * @protected
         * @type {Object}
         */
        this._handlers = {
            'initialized.owl.carousel': $.proxy(
                function () {
                    if ( this._core.settings.autoHeight ) {
                        this.update();
                    }
                }, this
            ),
            'changed.owl.carousel'    : $.proxy(
                function ( e ) {
                    if ( this._core.settings.autoHeight && e.property.name == 'position' ) {
                        this.update();
                    }
                }, this
            ),
            'loaded.owl.lazy'         : $.proxy(
                function ( e ) {
                    if ( this._core.settings.autoHeight && e.element.closest(
                            '.'
                            + this._core.settings.itemClass
                        )
                        === this._core.$stage.children().eq( this._core.current() ) ) {
                        this.update();
                    }
                }, this
            )
        };

        // set default options
        this._core.options =
            $.extend( {}, AutoHeight.Defaults, this._core.options );

        // register event handlers
        this._core.$element.on( this._handlers );
    };

    /**
     * Default options.
     * @public
     */
    AutoHeight.Defaults = {
        autoHeight     : false,
        autoHeightClass: 'owl-height'
    };

    /**
     * Updates the view.
     */
    AutoHeight.prototype.update = function () {
        this._core.$stage.parent()
            .height( this._core.$stage.children().eq( this._core.current() ).height() )
            .addClass( this._core.settings.autoHeightClass );
    };

    AutoHeight.prototype.destroy = function () {
        var handler, property;

        for ( handler in this._handlers ) {
            this._core.$element.off( handler, this._handlers[ handler ] );
        }
        for ( property in Object.getOwnPropertyNames( this ) ) {
            typeof this[ property ] != 'function' && ( this[ property ] = null );
        }
    };

    $.fn.owlCarousel.Constructor.Plugins.AutoHeight = AutoHeight;

})( window.Zepto || window.jQuery, window, document );

/**
 * Video Plugin
 * @version 2.0.0
 * @author Bartosz Wojciechowski
 * @license The MIT License (MIT)
 */
(function ( $, window, document, undefined ) {

    /**
     * Creates the video plugin.
     * @class The Video Plugin
     * @param {Owl} carousel - The Owl Carousel
     */
    var Video = function ( carousel ) {
        /**
         * Reference to the core.
         * @protected
         * @type {Owl}
         */
        this._core = carousel;

        /**
         * Cache all video URLs.
         * @protected
         * @type {Object}
         */
        this._videos = {};

        /**
         * Current playing item.
         * @protected
         * @type {jQuery}
         */
        this._playing = null;

        /**
         * Whether this is in fullscreen or not.
         * @protected
         * @type {Boolean}
         */
        this._fullscreen = false;

        /**
         * All event handlers.
         * @protected
         * @type {Object}
         */
        this._handlers = {
            'resize.owl.carousel'                      : $.proxy(
                function ( e ) {
                    if ( this._core.settings.video && ! this.isInFullScreen() ) {
                        e.preventDefault();
                    }
                }, this
            ),
            'refresh.owl.carousel changed.owl.carousel': $.proxy(
                function ( e ) {
                    if ( this._playing ) {
                        this.stop();
                    }
                }, this
            ),
            'prepared.owl.carousel'                    : $.proxy(
                function ( e ) {
                    var $element = $( e.content ).find( '.owl-video' );
                    if ( $element.length ) {
                        $element.css( 'display', 'none' );
                        this.fetch( $element, $( e.content ) );
                    }
                }, this
            )
        };

        // set default options
        this._core.options = $.extend( {}, Video.Defaults, this._core.options );

        // register event handlers
        this._core.$element.on( this._handlers );

        this._core.$element.on(
            'click.owl.video', '.owl-video-play-icon', $.proxy(
                function ( e ) {
                    this.play( e );
                }, this
            )
        );
    };

    /**
     * Default options.
     * @public
     */
    Video.Defaults = {
        video      : false,
        videoHeight: false,
        videoWidth : false
    };

    /**
     * Gets the video ID and the type (YouTube/Vimeo only).
     * @protected
     * @param {jQuery} target - The target containing the video data.
     * @param {jQuery} item - The item containing the video.
     */
    Video.prototype.fetch = function ( target, item ) {

        var type   = target.attr( 'data-vimeo-id' ) ? 'vimeo' : 'youtube',
            id     = target.attr( 'data-vimeo-id' ) || target.attr( 'data-youtube-id' ),
            width  = target.attr( 'data-width' ) || this._core.settings.videoWidth,
            height = target.attr( 'data-height' ) || this._core.settings.videoHeight,
            url    = target.attr( 'href' );

        if ( url ) {
            id =
                url.match( /(http:|https:|)\/\/(player.|www.)?(vimeo\.com|youtu(be\.com|\.be|be\.googleapis\.com))\/(video\/|embed\/|watch\?v=|v\/)?([A-Za-z0-9._%-]*)(\&\S+)?/ );

            if ( id[ 3 ].indexOf( 'youtu' ) > - 1 ) {
                type = 'youtube';
            }
            else if ( id[ 3 ].indexOf( 'vimeo' ) > - 1 ) {
                type = 'vimeo';
            }
            else {
                throw new Error( 'Video URL not supported.' );
            }
            id = id[ 6 ];
        }
        else {
            throw new Error( 'Missing video URL.' );
        }

        this._videos[ url ] = {
            type  : type,
            id    : id,
            width : width,
            height: height
        };

        item.attr( 'data-video', url );

        this.thumbnail( target, this._videos[ url ] );
    };

    /**
     * Creates video thumbnail.
     * @protected
     * @param {jQuery} target - The target containing the video data.
     * @param {Object} info - The video info object.
     * @see `fetch`
     */
    Video.prototype.thumbnail = function ( target, video ) {

        var tnLink,
            icon,
            path,
            dimensions = video.width && video.height ?
            'sass="width:' + video.width + 'px;height:' + video.height + 'px;"' : '',
            customTn   = target.find( 'img' ),
            srcType    = 'src',
            lazyClass  = '',
            settings   = this._core.settings,
            create     = function ( path ) {
                icon = '<div class="owl-video-play-icon"></div>';

                if ( settings.lazyLoad ) {
                    tnLink = '<div class="owl-video-tn ' + lazyClass + '" ' + srcType
                        + '="' + path + '"></div>';
                }
                else {
                    tnLink =
                        '<div class="owl-video-tn" sass="opacity:1;background-image:url('
                        + path + ')"></div>';
                }
                target.after( tnLink );
                target.after( icon );
            };

        // wrap video content into owl-video-wrapper div
        target.wrap( '<div class="owl-video-wrapper"' + dimensions + '></div>' );

        if ( this._core.settings.lazyLoad ) {
            srcType   = 'data-src';
            lazyClass = 'owl-lazy';
        }

        // custom thumbnail
        if ( customTn.length ) {
            create( customTn.attr( srcType ) );
            customTn.remove();
            return false;
        }

        if ( video.type === 'youtube' ) {
            path = "http://img.youtube.com/vi/" + video.id + "/hqdefault.jpg";
            create( path );
        }
        else if ( video.type === 'vimeo' ) {
            $.ajax(
                {
                    type    : 'GET',
                    url     : 'http://vimeo.com/api/v2/video/' + video.id + '.json',
                    jsonp   : 'callback',
                    dataType: 'jsonp',
                    success : function ( data ) {
                        path = data[ 0 ].thumbnail_large;
                        create( path );
                    }
                }
            );
        }
    };

    /**
     * Stops the current video.
     * @public
     */
    Video.prototype.stop = function () {
        this._core.trigger( 'stop', null, 'video' );
        this._playing.find( '.owl-video-frame' ).remove();
        this._playing.removeClass( 'owl-video-playing' );
        this._playing = null;
    };

    /**
     * Starts the current video.
     * @public
     * @param {Event} ev - The event arguments.
     */
    Video.prototype.play = function ( ev ) {
        this._core.trigger( 'play', null, 'video' );

        if ( this._playing ) {
            this.stop();
        }

        var target    = $( ev.target || ev.srcElement ),
            item      = target.closest( '.' + this._core.settings.itemClass ),
            video     = this._videos[ item.attr( 'data-video' ) ],
            width     = video.width || '100%',
            height    = video.height || this._core.$stage.height(),
            html, wrap;

        if ( video.type === 'youtube' ) {
            html = '<iframe width="' + width + '" height="' + height
                + '" src="http://www.youtube.com/embed/'
                + video.id + '?autoplay=1&v=' + video.id
                + '" frameborder="0" allowfullscreen></iframe>';
        }
        else if ( video.type === 'vimeo' ) {
            html = '<iframe src="##' + video.id
                + '?autoplay=1" width="' + width
                + '" height="' + height
                + '" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>';
        }

        item.addClass( 'owl-video-playing' );
        this._playing = item;

        wrap = $(
            '<div sass="height:' + height + 'px; width:' + width
            + 'px" class="owl-video-frame">'
            + html + '</div>'
        );
        target.after( wrap );
    };

    /**
     * Checks whether an video is currently in full screen mode or not.
     * @todo Bad sass because looks like a readonly method but changes members.
     * @protected
     * @returns {Boolean}
     */
    Video.prototype.isInFullScreen = function () {

        // if Vimeo Fullscreen mode
        var element = document.fullscreenElement || document.mozFullScreenElement
            || document.webkitFullscreenElement;

        if ( element && $( element ).parent().hasClass( 'owl-video-frame' ) ) {
            this._core.speed( 0 );
            this._fullscreen = true;
        }

        if ( element && this._fullscreen && this._playing ) {
            return false;
        }

        // comming back from fullscreen
        if ( this._fullscreen ) {
            this._fullscreen = false;
            return false;
        }

        // check full screen mode and window orientation
        if ( this._playing ) {
            if ( this._core.state.orientation !== window.orientation ) {
                this._core.state.orientation = window.orientation;
                return false;
            }
        }

        return true;
    };

    /**
     * Destroys the plugin.
     */
    Video.prototype.destroy = function () {
        var handler, property;

        this._core.$element.off( 'click.owl.video' );

        for ( handler in this._handlers ) {
            this._core.$element.off( handler, this._handlers[ handler ] );
        }
        for ( property in Object.getOwnPropertyNames( this ) ) {
            typeof this[ property ] != 'function' && ( this[ property ] = null );
        }
    };

    $.fn.owlCarousel.Constructor.Plugins.Video = Video;

})( window.Zepto || window.jQuery, window, document );

/**
 * Animate Plugin
 * @version 2.0.0
 * @author Bartosz Wojciechowski
 * @license The MIT License (MIT)
 */
(function ( $, window, document, undefined ) {

    /**
     * Creates the animate plugin.
     * @class The Navigation Plugin
     * @param {Owl} scope - The Owl Carousel
     */
    var Animate = function ( scope ) {
        this.core         = scope;
        this.core.options = $.extend( {}, Animate.Defaults, this.core.options );
        this.swapping     = true;
        this.previous     = undefined;
        this.next         = undefined;

        this.handlers = {
            'change.owl.carousel'                                           : $.proxy(
                function ( e ) {
                    if ( e.property.name == 'position' ) {
                        this.previous = this.core.current();
                        this.next     = e.property.value;
                    }
                }, this
            ),
            'drag.owl.carousel dragged.owl.carousel translated.owl.carousel': $.proxy(
                function ( e ) {
                    this.swapping = e.type == 'translated';
                }, this
            ),
            'translate.owl.carousel'                                        : $.proxy(
                function ( e ) {
                    if ( this.swapping && ( this.core.options.animateOut
                        || this.core.options.animateIn ) ) {
                        this.swap();
                    }
                }, this
            )
        };

        this.core.$element.on( this.handlers );
    };

    /**
     * Default options.
     * @public
     */
    Animate.Defaults = {
        animateOut: false,
        animateIn : false
    };

    /**
     * Toggles the animation classes whenever an translations starts.
     * @protected
     * @returns {Boolean|undefined}
     */
    Animate.prototype.swap = function () {

        if ( this.core.settings.items !== 1 || ! this.core.support3d ) {
            return;
        }

        this.core.speed( 0 );

        var left,
            clear    = $.proxy( this.clear, this ),
            previous = this.core.$stage.children().eq( this.previous ),
            next     = this.core.$stage.children().eq( this.next ),
            incoming = this.core.settings.animateIn,
            outgoing = this.core.settings.animateOut;

        if ( this.core.current() === this.previous ) {
            return;
        }

        if ( outgoing ) {
            left = this.core.coordinates( this.previous )
                - this.core.coordinates( this.next );
            previous.css(
                {
                    'left': left + 'px'
                }
            )
                .addClass( 'animated owl-animated-out' )
                .addClass( outgoing )
                .one( 'webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', clear );
        }

        if ( incoming ) {
            next.addClass( 'animated owl-animated-in' )
                .addClass( incoming )
                .one( 'webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', clear );
        }
    };

    Animate.prototype.clear = function ( e ) {
        $( e.target ).css(
            {
                'left': ''
            }
        )
            .removeClass( 'animated owl-animated-out owl-animated-in' )
            .removeClass( this.core.settings.animateIn )
            .removeClass( this.core.settings.animateOut );
        this.core.transitionEnd();
    }

    /**
     * Destroys the plugin.
     * @public
     */
    Animate.prototype.destroy = function () {
        var handler, property;

        for ( handler in this.handlers ) {
            this.core.$element.off( handler, this.handlers[ handler ] );
        }
        for ( property in Object.getOwnPropertyNames( this ) ) {
            typeof this[ property ] != 'function' && ( this[ property ] = null );
        }
    };

    $.fn.owlCarousel.Constructor.Plugins.Animate = Animate;

})( window.Zepto || window.jQuery, window, document );

/**
 * Autoplay Plugin
 * @version 2.0.0
 * @author Bartosz Wojciechowski
 * @license The MIT License (MIT)
 */
(function ( $, window, document, undefined ) {

    /**
     * Creates the autoplay plugin.
     * @class The Autoplay Plugin
     * @param {Owl} scope - The Owl Carousel
     */
    var Autoplay = function ( scope ) {
        this.core         = scope;
        this.core.options = $.extend( {}, Autoplay.Defaults, this.core.options );

        this.handlers = {
            'translated.owl.carousel refreshed.owl.carousel': $.proxy(
                function () {
                    this.autoplay();
                }, this
            ),
            'play.owl.autoplay'                             : $.proxy(
                function ( e, t, s ) {
                    this.play( t, s );
                }, this
            ),
            'stop.owl.autoplay'                             : $.proxy(
                function () {
                    this.stop();
                }, this
            ),
            'mouseover.owl.autoplay'                        : $.proxy(
                function () {
                    if ( this.core.settings.autoplayHoverPause ) {
                        this.pause();
                    }
                }, this
            ),
            'mouseleave.owl.autoplay'                       : $.proxy(
                function () {
                    if ( this.core.settings.autoplayHoverPause ) {
                        this.autoplay();
                    }
                }, this
            )
        };

        this.core.$element.on( this.handlers );
    };

    /**
     * Default options.
     * @public
     */
    Autoplay.Defaults = {
        autoplay          : false,
        autoplayTimeout   : 5000,
        autoplayHoverPause: false,
        autoplaySpeed     : false
    };

    /**
     * @protected
     * @todo Must be documented.
     */
    Autoplay.prototype.autoplay = function () {
        if ( this.core.settings.autoplay && ! this.core.state.videoPlay ) {
            window.clearInterval( this.interval );

            this.interval = window.setInterval(
                $.proxy(
                    function () {
                        this.play();
                    }, this
                ), this.core.settings.autoplayTimeout
            );
        }
        else {
            window.clearInterval( this.interval );
        }
    };

    /**
     * Starts the autoplay.
     * @public
     * @param {Number} [timeout] - ...
     * @param {Number} [speed] - ...
     * @returns {Boolean|undefined} - ...
     * @todo Must be documented.
     */
    Autoplay.prototype.play = function ( timeout, speed ) {
        // if tab is inactive - doesnt work in <IE10
        if ( document.hidden === true ) {
            return;
        }

        if ( this.core.state.isTouch || this.core.state.isScrolling
            || this.core.state.isSwiping || this.core.state.inMotion ) {
            return;
        }

        if ( this.core.settings.autoplay === false ) {
            window.clearInterval( this.interval );
            return;
        }

        this.core.next( this.core.settings.autoplaySpeed );
    };

    /**
     * Stops the autoplay.
     * @public
     */
    Autoplay.prototype.stop = function () {
        window.clearInterval( this.interval );
    };

    /**
     * Pauses the autoplay.
     * @public
     */
    Autoplay.prototype.pause = function () {
        window.clearInterval( this.interval );
    };

    /**
     * Destroys the plugin.
     */
    Autoplay.prototype.destroy = function () {
        var handler, property;

        window.clearInterval( this.interval );

        for ( handler in this.handlers ) {
            this.core.$element.off( handler, this.handlers[ handler ] );
        }
        for ( property in Object.getOwnPropertyNames( this ) ) {
            typeof this[ property ] != 'function' && ( this[ property ] = null );
        }
    };

    $.fn.owlCarousel.Constructor.Plugins.autoplay = Autoplay;

})( window.Zepto || window.jQuery, window, document );

/**
 * Navigation Plugin
 * @version 2.0.0
 * @author Artus Kolanowski
 * @license The MIT License (MIT)
 */
(function ( $, window, document, undefined ) {
    'use strict';

    /**
     * Creates the navigation plugin.
     * @class The Navigation Plugin
     * @param {Owl} carousel - The Owl Carousel.
     */
    var Navigation = function ( carousel ) {
        /**
         * Reference to the core.
         * @protected
         * @type {Owl}
         */
        this._core = carousel;

        /**
         * Indicates whether the plugin is initialized or not.
         * @protected
         * @type {Boolean}
         */
        this._initialized = false;

        /**
         * The current paging indexes.
         * @protected
         * @type {Array}
         */
        this._pages = [];

        /**
         * All DOM elements of the user interface.
         * @protected
         * @type {Object}
         */
        this._controls = {};

        /**
         * Markup for an indicator.
         * @protected
         * @type {Array.<String>}
         */
        this._templates = [];

        /**
         * The carousel element.
         * @type {jQuery}
         */
        this.$element = this._core.$element;

        /**
         * Overridden methods of the carousel.
         * @protected
         * @type {Object}
         */
        this._overrides = {
            next: this._core.next,
            prev: this._core.prev,
            to  : this._core.to
        };

        /**
         * All event handlers.
         * @protected
         * @type {Object}
         */
        this._handlers = {
            'prepared.owl.carousel'                    : $.proxy(
                function ( e ) {
                    if ( this._core.settings.dotsData ) {
                        this._templates.push( $( e.content ).find( '[data-dot]' ).andSelf( '[data-dot]' ).attr( 'data-dot' ) );
                    }
                }, this
            ),
            'add.owl.carousel'                         : $.proxy(
                function ( e ) {
                    if ( this._core.settings.dotsData ) {
                        this._templates.splice( e.position, 0, $( e.content ).find( '[data-dot]' ).andSelf( '[data-dot]' ).attr( 'data-dot' ) );
                    }
                }, this
            ),
            'remove.owl.carousel prepared.owl.carousel': $.proxy(
                function ( e ) {
                    if ( this._core.settings.dotsData ) {
                        this._templates.splice( e.position, 1 );
                    }
                }, this
            ),
            'change.owl.carousel'                      : $.proxy(
                function ( e ) {
                    if ( e.property.name == 'position' ) {
                        if ( ! this._core.state.revert && ! this._core.settings.loop
                            && this._core.settings.navRewind ) {
                            var current = this._core.current(),
                                maximum = this._core.maximum(),
                                minimum = this._core.minimum();
                            e.data      = e.property.value > maximum
                                ? current >= maximum ? minimum : maximum
                                : e.property.value < minimum ? maximum : e.property.value;
                        }
                    }
                }, this
            ),
            'changed.owl.carousel'                     : $.proxy(
                function ( e ) {
                    if ( e.property.name == 'position' ) {
                        this.draw();
                    }
                }, this
            ),
            'refreshed.owl.carousel'                   : $.proxy(
                function () {
                    if ( ! this._initialized ) {
                        this.initialize();
                        this._initialized = true;
                    }
                    this._core.trigger( 'refresh', null, 'navigation' );
                    this.update();
                    this.draw();
                    this._core.trigger( 'refreshed', null, 'navigation' );
                }, this
            )
        };

        // set default options
        this._core.options =
            $.extend( {}, Navigation.Defaults, this._core.options );

        // register event handlers
        this.$element.on( this._handlers );
    }

    /**
     * Default options.
     * @public
     * @todo Rename `slideBy` to `navBy`
     */
    Navigation.Defaults = {
        nav              : false,
        navRewind        : true,
        navText          : [ 'prev', 'next' ],
        navSpeed         : false,
        navElement       : 'div',
        navContainer     : false,
        navContainerClass: 'owl-nav',
        navClass         : [ 'owl-prev', 'owl-next' ],
        slideBy          : 1,
        dotClass         : 'owl-dot',
        dotsClass        : 'owl-dots',
        dots             : true,
        dotsEach         : false,
        dotData          : false,
        dotsSpeed        : false,
        dotsContainer    : false,
        controlsClass    : 'owl-controls'
    }

    /**
     * Initializes the layout of the plugin and extends the carousel.
     * @protected
     */
    Navigation.prototype.initialize = function () {
        var $container, override,
            options = this._core.settings;

        // create the indicator template
        if ( ! options.dotsData ) {
            this._templates = [ $( '<div>' )
                .addClass( options.dotClass )
                .append( $( '<span>' ) )
                .prop( 'outerHTML' ) ];
        }

        // create controls container if needed
        if ( ! options.navContainer || ! options.dotsContainer ) {
            this._controls.$container = $( '<div>' )
                .addClass( options.controlsClass )
                .appendTo( this.$element );
        }

        // create DOM structure for absolute navigation
        this._controls.$indicators = options.dotsContainer ?
            $( options.dotsContainer )
            :
            $( '<div>' ).hide().addClass( options.dotsClass ).appendTo( this._controls.$container );

        this._controls.$indicators.on(
            'click', 'div', $.proxy(
                function ( e ) {
                    var index = $( e.target ).parent().is( this._controls.$indicators )
                        ? $( e.target ).index() : $( e.target ).parent().index();

                    e.preventDefault();

                    this.to( index, options.dotsSpeed );
                }, this
            )
        );

        // create DOM structure for relative navigation
        $container = options.navContainer ? $( options.navContainer )
            :
            $( '<div>' ).addClass( options.navContainerClass ).prependTo( this._controls.$container );

        this._controls.$next     = $( '<' + options.navElement + '>' );
        this._controls.$previous = this._controls.$next.clone();

        this._controls.$previous
            .addClass( options.navClass[ 0 ] )
            .html( options.navText[ 0 ] )
            .hide()
            .prependTo( $container )
            .on(
            'click', $.proxy(
                function ( e ) {
                    this.prev( options.navSpeed );
                }, this
            )
        );
        this._controls.$next
            .addClass( options.navClass[ 1 ] )
            .html( options.navText[ 1 ] )
            .hide()
            .appendTo( $container )
            .on(
            'click', $.proxy(
                function ( e ) {
                    this.next( options.navSpeed );
                }, this
            )
        );

        // override public methods of the carousel
        for ( override in this._overrides ) {
            this._core[ override ] = $.proxy( this[ override ], this );
        }
    }

    /**
     * Destroys the plugin.
     * @protected
     */
    Navigation.prototype.destroy = function () {
        var handler, control, property, override;

        for ( handler in this._handlers ) {
            this.$element.off( handler, this._handlers[ handler ] );
        }
        for ( control in this._controls ) {
            this._controls[ control ].remove();
        }
        for ( override in this.overides ) {
            this._core[ override ] = this._overrides[ override ];
        }
        for ( property in Object.getOwnPropertyNames( this ) ) {
            typeof this[ property ] != 'function' && ( this[ property ] = null );
        }
    }

    /**
     * Updates the internal state.
     * @protected
     */
    Navigation.prototype.update = function () {
        var i, j, k,
            options = this._core.settings,
            lower   = this._core.clones().length / 2,
            upper   = lower + this._core.items().length,
            size    = options.center || options.autoWidth || options.dotData
                ? 1 : options.dotsEach || options.items;

        if ( options.slideBy !== 'page' ) {
            options.slideBy = Math.min( options.slideBy, options.items );
        }

        if ( options.dots || options.slideBy == 'page' ) {
            this._pages = [];

            for ( i = lower, j = 0, k = 0; i < upper; i ++ ) {
                if ( j >= size || j === 0 ) {
                    this._pages.push(
                        {
                            start: i - lower,
                            end  : i - lower + size - 1
                        }
                    );
                    j = 0, ++ k;
                }
                j += this._core.mergers( this._core.relative( i ) );
            }
        }
    }

    /**
     * Draws the user interface.
     * @todo The option `dotData` wont work.
     * @protected
     */
    Navigation.prototype.draw = function () {
        var difference, i, html = '',
            options             = this._core.settings,
            $items              = this._core.$stage.children(),
            index               = this._core.relative( this._core.current() );

        if ( options.nav && ! options.loop && ! options.navRewind ) {
            this._controls.$previous.toggleClass( 'disabled', index <= 0 );
            this._controls.$next.toggleClass(
                'disabled', index
                >= this._core.maximum()
            );
        }

        this._controls.$previous.toggle( options.nav );
        this._controls.$next.toggle( options.nav );

        if ( options.dots ) {
            difference = this._pages.length
                - this._controls.$indicators.children().length;

            if ( options.dotData && difference !== 0 ) {
                for ( i = 0; i < this._controls.$indicators.children().length; i ++ ) {
                    html += this._templates[ this._core.relative( i ) ];
                }
                this._controls.$indicators.html( html );
            }
            else if ( difference > 0 ) {
                html = new Array( difference + 1 ).join( this._templates[ 0 ] );
                this._controls.$indicators.append( html );
            }
            else if ( difference < 0 ) {
                this._controls.$indicators.children().slice( difference ).remove();
            }

            this._controls.$indicators.find( '.active' ).removeClass( 'active' );
            this._controls.$indicators.children().eq( $.inArray( this.current(), this._pages ) ).addClass( 'active' );
        }

        this._controls.$indicators.toggle( options.dots );
    }

    /**
     * Extends event data.
     * @protected
     * @param {Event} event - The event object which gets thrown.
     */
    Navigation.prototype.onTrigger = function ( event ) {
        var settings = this._core.settings;

        event.page = {
            index: $.inArray( this.current(), this._pages ),
            count: this._pages.length,
            size : settings && ( settings.center || settings.autoWidth
            || settings.dotData
                ? 1 : settings.dotsEach || settings.items )
        };
    }

    /**
     * Gets the current page position of the carousel.
     * @protected
     * @returns {Number}
     */
    Navigation.prototype.current = function () {
        var index = this._core.relative( this._core.current() );
        return $.grep(
            this._pages, function ( o ) {
                return o.start <= index && o.end >= index;
            }
        ).pop();
    }

    /**
     * Gets the current succesor/predecessor position.
     * @protected
     * @returns {Number}
     */
    Navigation.prototype.getPosition = function ( successor ) {
        var position, length,
            options = this._core.settings;

        if ( options.slideBy == 'page' ) {
            position = $.inArray( this.current(), this._pages );
            length   = this._pages.length;
            successor ? ++ position : -- position;
            position = this._pages[ ( ( position % length ) + length ) % length ].start;
        }
        else {
            position = this._core.relative( this._core.current() );
            length   = this._core.items().length;
            successor ? position += options.slideBy : position -= options.slideBy;
        }
        return position;
    }

    /**
     * Slides to the next item or page.
     * @public
     * @param {Number} [speed=false] - The time in milliseconds for the transition.
     */
    Navigation.prototype.next = function ( speed ) {
        $.proxy( this._overrides.to, this._core )( this.getPosition( true ), speed );
    }

    /**
     * Slides to the previous item or page.
     * @public
     * @param {Number} [speed=false] - The time in milliseconds for the transition.
     */
    Navigation.prototype.prev = function ( speed ) {
        $.proxy( this._overrides.to, this._core )( this.getPosition( false ), speed );
    }

    /**
     * Slides to the specified item or page.
     * @public
     * @param {Number} position - The position of the item or page.
     * @param {Number} [speed] - The time in milliseconds for the transition.
     * @param {Boolean} [standard=false] - Whether to use the standard behaviour or not.
     */
    Navigation.prototype.to = function ( position, speed, standard ) {
        var length;

        if ( ! standard ) {
            length = this._pages.length;
            $.proxy( this._overrides.to, this._core )(
                this._pages[ ( ( position
                % length ) + length ) % length ].start, speed
            );
        }
        else {
            $.proxy( this._overrides.to, this._core )( position, speed );
        }
    }

    $.fn.owlCarousel.Constructor.Plugins.Navigation = Navigation;

})( window.Zepto || window.jQuery, window, document );

/**
 * Hash Plugin
 * @version 2.0.0
 * @author Artus Kolanowski
 * @license The MIT License (MIT)
 */
(function ( $, window, document, undefined ) {
    'use strict';

    /**
     * Creates the hash plugin.
     * @class The Hash Plugin
     * @param {Owl} carousel - The Owl Carousel
     */
    var Hash = function ( carousel ) {
        /**
         * Reference to the core.
         * @protected
         * @type {Owl}
         */
        this._core = carousel;

        /**
         * Hash table for the hashes.
         * @protected
         * @type {Object}
         */
        this._hashes = {};

        /**
         * The carousel element.
         * @type {jQuery}
         */
        this.$element = this._core.$element;

        /**
         * All event handlers.
         * @protected
         * @type {Object}
         */
        this._handlers = {
            'initialized.owl.carousel': $.proxy(
                function () {
                    if ( this._core.settings.startPosition == 'URLHash' ) {
                        $( window ).trigger( 'hashchange.owl.navigation' );
                    }
                }, this
            ),
            'prepared.owl.carousel'   : $.proxy(
                function ( e ) {
                    var hash             =
                        $( e.content ).find( '[data-hash]' ).andSelf( '[data-hash]' ).attr( 'data-hash' );
                    this._hashes[ hash ] = e.content;
                }, this
            )
        };

        // set default options
        this._core.options = $.extend( {}, Hash.Defaults, this._core.options );

        // register the event handlers
        this.$element.on( this._handlers );

        // register event listener for hash navigation
        $( window ).on(
            'hashchange.owl.navigation', $.proxy(
                function () {
                    var hash     = window.location.hash.substring( 1 ),
                        items    = this._core.$stage.children(),
                        position = this._hashes[ hash ] && items.index( this._hashes[ hash ] ) || 0;

                    if ( ! hash ) {
                        return false;
                    }

                    this._core.to( position, false, true );
                }, this
            )
        );
    }

    /**
     * Default options.
     * @public
     */
    Hash.Defaults = {
        URLhashListener: false
    }

    /**
     * Destroys the plugin.
     * @public
     */
    Hash.prototype.destroy = function () {
        var handler, property;

        $( window ).off( 'hashchange.owl.navigation' );

        for ( handler in this._handlers ) {
            this._core.$element.off( handler, this._handlers[ handler ] );
        }
        for ( property in Object.getOwnPropertyNames( this ) ) {
            typeof this[ property ] != 'function' && ( this[ property ] = null );
        }
    }

    $.fn.owlCarousel.Constructor.Plugins.Hash = Hash;

})( window.Zepto || window.jQuery, window, document );